import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';

import { Hero } from './hero';
import { HEROES } from './mock-heroes';
import { MessageService } from './message.service';

const httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

@Injectable({
    providedIn: 'root'
})
export class HeroService {

    heroesUrl = 'api/heroes';

    constructor(private messageService: MessageService, private http: HttpClient) { }

    /**
     * 获取英雄
     * @param id 编号
     */
    getHeroAsync(id: number): Observable<Hero> {
        // return of(HEROES.find(t => t.id === id));
        const url = `${this.heroesUrl}/${id}`;
        return this.http.get<Hero>(url).pipe(
            tap(_ => this.log(`HeroService: getHero - ${id}`)),
            catchError(this.handleError<Hero>(`HeroService: getHero - ${id}`))
        );
    }

    /**
     * 获取英雄列表
     */
    getHeoresAsync(): Observable<Hero[]> {
        return this
            .http
            .get<Hero[]>(this.heroesUrl)
            .pipe(
                tap(_ => this.log('HeroService: getHeoresAsync')),
                catchError(this.handleError<Hero[]>('HeroService: getHeoresAsync', [])));
        // return of(HEROES);
    }

    addHeroAsync(hero: Hero): Observable<Hero> {
        return this.http
            .post<Hero>(this.heroesUrl, hero, httpOptions)
            .pipe(
                tap(t => this.log(`addHeroAsync - id: ${t.id}`)),
                catchError(this.handleError<Hero>(`addHeroAsync - ${hero.name}`))
            );
    }

    /**
     * 更新英雄
     * @param hero - 英雄
     */
    updateHeorAsync(hero: Hero): Observable<Hero> {
        return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
            tap(_ => {
                this.log(`updateHeorAsync - ${hero.id}`);
            }),
            catchError(this.handleError<any>(`HeroService: updateHeorAsync - ${hero.id}`))
        );
    }

    /**
     * 删除英雄
     * @param id - 编号
     */
    deleteHeroAsync(hero: Hero | number): Observable<any> {
        const id = (typeof hero === 'number') ? hero : hero.id;
        const url = `${this.heroesUrl}/${id}`;
        return this.http.delete(url).pipe(
            tap(_ => this.log(`deleteHeroAsync - ${id}`)),
            catchError(this.handleError<any>(`updateHeorAsync - ${id}`))
        );
    }

    /**
     * 查找英雄
     * @param term - 英雄名称
     */
    searchHeroesAsync(term: string): Observable<Hero[]> {
        if (!term.trim()) {
            return of([]);
        }

        return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
            tap(_ => this.log(`searchHeroesAsync - "${term}"`)),
            catchError(this.handleError<Hero[]>('searchHeroesAsync', []))
        );
    }

    /**
     * Handle Http operation that failed.
     * Let the app continue.
     * @param operation - name of the operation that failed
     * @param result - optional value to return as the observable result
     */
    handleError<T>(operation = 'operation', result?: T) {
        return (error: any): Observable<T> => {

            // TODO: send the error to remote logging infrastructure
            console.error(error); // log to console instead

            // TODO: better job of transforming error for user consumption
            this.log(`${operation} failed: ${error.message} - ${error.status} - ${error.statusText}`);

            // Let the app keep running by returning an empty result.
            return of(result as T);
        };
    }

    log(msg: string) {
        this.messageService.add(msg);
    }

}
